<?php

require_once __DIR__ . '/Container.php';

/**
 * 应用处理
 * @author ice.deng <ice.deng@qq.com>
 *
 */
class Application
{
    /**
     * 基础路径
     * @var string
     */
    public $baseUrl = '';
    /**
     * url路由的参数名称
     * @var string
     */
    public $routeParamKey = 'r';
    /**
     * 容器
     * @var Container
     */
    public $ci;
    /**
     * @var IRenderer
     */
    public $view;
    /**
     * 路由列表
     * @var array
     */
    private $routes = [];

    public function __construct($config = [])
    {
        $this->ci = new Container($config);
        if (isset($config['baseUrl'])) {
            $this->baseUrl = $config['baseUrl'];
        }
        $this->ci['app'] = $this;
        $this->ci['404'] = function ($ci) {
            echo 'Url(' . $ci->app->getPathInfo() . ') not find.';
        };
    }

    public function getCi()
    {
        return $this->ci;
    }

    /**
     * 是否为XMLHttpRequest请求
     * @return bool
     */
    public function isXhrRequest()
    {
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
    }

    /**
     * 添加路由
     * @param $routes
     */
    public function addRoutes($routes)
    {
        foreach ($routes as $k => $v) {
            $this->routes[$this->baseUrl . $k] = $v;
        }
    }

    /**
     * 创建一个URL地址
     * @param null $path
     * @param array $params
     * @param bool $absolute
     * @return null|string
     */
    public function createUri($path = null, $params = [], $absolute = false)
    {
        if (strpos($path, '://') > 0)
            return $path;
        $baseUrl = rtrim($this->baseUrl, '/');
        if (empty($path)) {
            $uri = $baseUrl . '/';
        } else {
            if ($this->ci['rewrite'])
                $uri = $baseUrl . '/' . $path . (false === strpos($path, '#') ? '/' : '');
            else
                $uri = $baseUrl . '/index.php?' . $this->routeParamKey . '=' . trim($path, '/');
        }
        if ($absolute)
            $uri = $this->getAbsoluteUri($uri);
        // 添加参数
        $q = [];
        foreach ($params as $k => $v) {
            $q[] = $k . '=' . urlencode($v);
        }
        if (count($q) > 0) {
            $uri .= (strpos($uri, '?') > 0 ? '&' : '?') . implode('&', $q);
        }
        return $uri;
    }

    /**
     * 获取资源文件的路径
     * @param null|string $path
     * @param bool $absolute
     * @return mixed|string
     */
    public function getAssetsUri($path = null, $absolute = false)
    {
        $baseUrl = $this->ci['assets_url'];
        if (empty($path)) {
            return $baseUrl;
        } else {
            $uri = $baseUrl . '/' . ltrim($path, '/');
        }
        if ($absolute)
            $uri = $this->getAbsoluteUri($uri);
        return $uri;
    }

    /**
     * 获取绝对地址
     * @param $uri
     * @return string
     */
    public function getAbsoluteUri($uri)
    {
        $protocol = (isset($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS']) ? 'https://' : 'http://';
        $port = (
            isset($_SERVER['SERVER_PORT']) &&
            (('http' == $protocol && $_SERVER['SERVER_PORT'] != '80') ||
                ('https' == $protocol && $_SERVER['SERVER_PORT'] != '443'))
        ) ? ':' . $_SERVER['SERVER_PORT'] : null;
        return $protocol . $_SERVER['SERVER_NAME'] . $port . $uri;
    }

    /**
     * 获取访问路径
     * @return string
     */
    private function getPathInfo()
    {
        $pathInfo = '/';
        if (!empty($_SERVER['PATH_INFO'])) {
            $pathInfo = $_SERVER['PATH_INFO'];
        } elseif (!empty($_SERVER['ORIG_PATH_INFO']) && $_SERVER['ORIG_PATH_INFO'] !== '/index.php') {
            $pathInfo = $_SERVER['ORIG_PATH_INFO'];
        } else {
            if (!empty($_SERVER['REQUEST_URI']) &&
                strpos($_SERVER['REQUEST_URI'], '?') > 0 &&
                isset($_GET[$this->routeParamKey])
                && !empty($_GET[$this->routeParamKey])
            ) {
                $pathInfo = $_GET[$this->routeParamKey];
            }
        }
        return $pathInfo;
    }

    public function render($templatePath, $data)
    {

    }

    /**
     * 开始运行
     */
    public function run()
    {
        $ci = $this->ci;
        if ($ci->offsetExists('before_request')) {
            $ci['before_request']($ci);
        }
        // 请求路径
        $pathInfo = $this->getPathInfo();
        // 请求方式
        $requestMethod = strtolower($_SERVER['REQUEST_METHOD']);

        // 匹配路由
        $routes = $this->routes;
        $handler = null;
        $regexMatches = [];
        if (isset($routes[$pathInfo])) {
            $handler = $routes[$pathInfo];
        } elseif ($routes) {
            $tokens = array(
                ':string' => '([a-zA-Z]+)',
                ':number' => '([0-9]+)',
                ':alpha' => '([a-zA-Z0-9-_]+)'
            );
            foreach ($routes as $pattern => $handlerSetting) {
                $pattern = strtr($pattern, $tokens);
                if (preg_match('#^/?' . $pattern . '/?$#', $pathInfo, $matches)) {
                    $handler = $handlerSetting;
                    $regexMatches = $matches;
                    break;
                }
            }
        }

        $response = null;
        $handlerInstance = null;

        // 实例化对象
        if ($handler) {
            if (is_string($handler)) {
                $handlerInstance = new $handler($ci);
            } elseif (is_callable($handler)) {
                $handlerInstance = $handler($ci);
            } elseif (is_array($handler)) {
                $handlerInstance = new $handler['class']($ci);
                unset($handler['class']);
                foreach ($handler as $k => $v) {
                    $handlerInstance->$k = $v;
                }
            }
        }

        if ($handlerInstance) {
            unset($regexMatches[0]);

            if ($this->isXhrRequest() && method_exists($handlerInstance, $requestMethod . '_xhr')) {
                header('Content-type: application/json');
                header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
                header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
                header('Cache-Control: no-store, no-cache, must-revalidate');
                header('Cache-Control: post-check=0, pre-check=0', false);
                header('Pragma: no-cache');
                $requestMethod .= '_xhr';
            }

            if (method_exists($handlerInstance, $requestMethod)) {
                if ($ci->offsetExists('before_handler')) {
                    $ci['before_handler']($ci, [
                        'handler' => $handlerInstance,
                        'requestMethod' => $requestMethod,
                        'regexMatches' => $regexMatches,
                    ]);
                }
                ob_start();
                $response = call_user_func_array([$handlerInstance, $requestMethod], $regexMatches);
                $output = ob_get_clean();
                if ($response == null) {
                    $response = $output;
                }
                if ($ci->offsetExists('after_handler')) {
                    $ci['after_handler']($ci, $response, [
                        'handler' => $handlerInstance,
                        'requestMethod' => $requestMethod,
                        'regexMatches' => $regexMatches,
                    ]);
                }
                echo $response;
            } else {
                call_user_func($ci['404'], $ci);
            }
        } else {
            call_user_func($ci['404'], $ci);
        }
    }
}